
package com.hcloud.auth.config;

import com.hcloud.auth.api.handler.MyAccessDeniedHandler;
import com.hcloud.auth.service.MyJdbcClientDetailsService;
import com.hcloud.auth.service.MyUserDetailsService;
import com.hcloud.auth.api.token.MyJwtAccessTokenConverter;
import com.hcloud.common.core.constants.AuthConstants;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import javax.sql.DataSource;

/**
 * @author hepangui
 * @Date 2018/10/28
 * 授权服务器
 * https://www.cnblogs.com/xingxueliao/p/5911292.html
 * https://blog.csdn.net/u013815546/article/details/76898524
 * 使用jwt：
 * https://yq.aliyun.com/articles/263390
 * 为什么使用jwt：
 * https://www.cnblogs.com/lihaoyang/p/8578440.html
 * 一个开源的项目中的做法
 * https://www.cnblogs.com/fp2952/p/8973613.html
 */
@Configuration
@AllArgsConstructor
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    private final DataSource dataSource;
    private final MyUserDetailsService myUserDetailsService;
    private final AuthenticationManager authenticationManager;
    private final WebResponseExceptionTranslator myOAuthExceptionTranslator;
    private final MyAccessDeniedHandler myAccessDeniedHandler;
//    private final RedisConnectionFactory redisConnectionFactory;

    /**
     * ClientDetailsServiceConfigurer (AuthorizationServerConfigurer 的一个回调配置项)
     * 能够使用内存或者JDBC来实现客户端详情服务（ClientDetailsService），
     * Spring Security OAuth2的配置方法是编写@Configuration类继承AuthorizationServerConfigurerAdapter，
     * 然后重写void configure(ClientDetailsServiceConfigurer clients)方法，
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//        clients.inMemory()
//                .withClient("app")
//                .scopes("server")
//                .resourceIds("auth","system")
//                .secret("$2a$10$EMPi3eYayp81cJwwv.gZmuKmSthC/ccnGwVLrs6TMUqiQEFbBpuR6")
//                .authorizedGrantTypes("password", "authorization_code", "refresh_token")
//                .and()
//                .withClient("app1")
//                .scopes("server")
//                .authorizedGrantTypes("password");


        clients.withClientDetails(new MyJdbcClientDetailsService(dataSource));
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
        //enable client to get the authenticated when using the /oauth/token to get a access token
        //there is a 401 authentication is required if it doesn't allow form authentication for clients when access /oauth/token
        oauthServer
                .allowFormAuthenticationForClients()
                // 开启/oauth/token_key验证端口无权限访问
                .tokenKeyAccess("permitAll()")
                // 开启/oauth/check_token验证端口认证权限访问
                .checkTokenAccess("permitAll()")
                .accessDeniedHandler(myAccessDeniedHandler);
    }

    /**
     * jwtAccessTokenConverter是用来生成token的转换器，而token令牌默认是有签名的，且资源服务器需要验证这个签名
     *
     * @param endpoints
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {

        /**
         * 使用JWT ，有两个增强器：
         *  1，使用JwtAccessTokenConverter将uuid的token转为jwt，用秘钥签名
         *  2，由于默认生成uuid token的方法是private，所以通过tokenEnhancer() 创建TokenEnhancer 往jwt里添加一些自定义的信息
         *  在这里拿到增强器的链，把这两个增强器连起来
         *  不再此处处理，转到jwtAccessTokenConvert中
         */
        //token增强配置
//        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
//        tokenEnhancerChain.setTokenEnhancers(
//                Arrays.asList(tokenEnhancer(), jwtAccessTokenConverter()));

        endpoints.authenticationManager(authenticationManager)
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
                // 配置JwtAccessToken转换器
                .accessTokenConverter(jwtAccessTokenConverter())
                // refresh_token需要userDetailsService
                .reuseRefreshTokens(false).userDetailsService(myUserDetailsService)

//                .tokenEnhancer(tokenEnhancerChain)
                .tokenStore(tokenStore())
                .exceptionTranslator(myOAuthExceptionTranslator);
        //.tokenStore(getJdbcTokenStore());
    }


//    @Bean
//    @Primary
    public TokenStore tokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    /**
     * 此处使用简单的对称加密，如果需要非对称加密可参考上述文章
     *
     * @return
     */
//    @Bean
//    @Primary
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new MyJwtAccessTokenConverter();
        converter.setSigningKey(AuthConstants.JWT_SIGNKEY);
        return converter;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


}
